package com.darin.camera;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.RelativeLayout;

public class CameraManager implements SurfaceHolder.Callback {
	private static final String TAG = "CameraManager";
	private Context context;
	private Camera camera;
	private SurfaceView surfaceView;
	private RelativeLayout contentLayout;
	private Rect frameRect;

	private int surfaceHeight;
	private int surfaceWidth;
	private int surfaceTop;
	private int surfaceLeft;
	private Point screenResolution;
	private WindowManager wm;
	private boolean hasStartPrevious = false;
	private boolean hasInitCamera = false;
	private CameraManagerListener listener;
	private int openCameraId = 0;
	private Size bestPreviewSize;

	public CameraManager(Context context, RelativeLayout view, WindowManager wm) {
		super();
		this.context = context;
		this.contentLayout = view;
		this.wm = wm;

	}

	public void init() {
		releaseCamera();
		if (!isInitCamera() && !isStartPrevious()) {
			
			openCamera();
			bestPreviewSize = getBestPreviewSize();
			setupSurfaceView(bestPreviewSize);
		}
	}

	/**
	 * 设置SurfaceView size;
	 * 
	 * @param size
	 */
	private void setupSurfaceView(Size size) {
		if (size == null) {
			Log.i(TAG, "setupSurfaceView>>size==null");
			return;
		}
		contentLayout.removeAllViews();
		int pvHeight = size.height;
		int pvWidth = size.width;
		RelativeLayout.LayoutParams lp = null;
		if (isLandSpace()) {
			lp = new RelativeLayout.LayoutParams(pvWidth, pvHeight);
		} else {
			int top = pvHeight >= screenResolution.x ? 0 : (screenResolution.x - pvHeight) / 2;
			int left = pvWidth >= screenResolution.y ? 0 : (screenResolution.y - pvWidth) / 2;
			surfaceTop = top;
			surfaceLeft = left;
			lp = new RelativeLayout.LayoutParams(pvHeight, pvWidth);

		}
		lp.addRule(RelativeLayout.CENTER_IN_PARENT);
		surfaceView = new SurfaceView(context);
		contentLayout.addView(surfaceView, lp);
		surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		surfaceView.getHolder().addCallback(this);
		Log.i(TAG, "setup surfaceview success>>" + lp.topMargin + " " + lp.leftMargin);
	}

	/**
	 * 打开camera
	 */
	private void openCamera() {
		try {
			if (hasCameraHardware()) {
				if (Build.VERSION.SDK_INT == 8) {
					camera = Camera.open();
				} else {
					camera = Camera.open(openCameraId);
				}
			}
		} catch (Exception e) {
			if (null != camera) {
				camera.release();
				camera = null;
			}
			// Log.i(TAG + "initCamera", ex.printStackTrace());
			e.printStackTrace();
		}
	}

	/**
	 * 设置camera属性
	 * 
	 * @param previewSize
	 */
	private void setupCamera(Size previewSize) {
		Log.d(TAG, "surface view >>" + surfaceView.getLeft() + "," + surfaceView.getTop() + "," + surfaceView.getRight() + "," + surfaceView.getBottom());
		if (camera == null) {
			return;
		}
		hasInitCamera = false;
		Log.i(TAG, "going into initCamera");
		if (hasStartPrevious) {
			stopPreView();
		}
		if (camera != null) {
			Camera.Parameters parameters = camera.getParameters();

			if (previewSize == null) {
				previewSize = getBestPreviewSize();
			}

			setupFrameRect();
			// 横竖屏镜头自动调整
			if (!isLandSpace()) {
				parameters.set("orientation", "portrait");
				// camera.setDisplayOrientation(90); // 在2.2以上可以使用
			} else {
				// // 如果是横屏
				parameters.set("orientation", "landscape"); //
			}

			camera.setDisplayOrientation(getCameraOrientation());

			// 指定拍照图片的大小
			parameters.setPreviewSize(previewSize.width, previewSize.height); // 指定preview的大小
			camera.setParameters(parameters);

			// 【调试】设置后的图片大小和预览大小以及帧率
			Camera.Size csize = camera.getParameters().getPreviewSize();
			Log.i(TAG + "initCamera", "after setting, previewSize:width: " + csize.width + " height: " + csize.height);
			csize = camera.getParameters().getPictureSize();
			Log.i(TAG, "after setting, orientaion>>" + getCameraOrientation());
			Log.i(TAG, "after setting, pictruesize:width: " + csize.width + " height: " + csize.height);
			Log.i(TAG, "after setting, previewformate is " + camera.getParameters().getPreviewFormat());
			Log.i(TAG, "after setting, previewframetate is " + camera.getParameters().getPreviewFrameRate());
			Log.i(TAG, "after settting surface width>>" + surfaceWidth + " height>>" + surfaceHeight);
			if (listener != null) {
				listener.initCameraSuccess();
			}
			hasInitCamera = true;
		}
	}

	/**
	 * 获取对这个resolution最好的camera preview size
	 * 
	 * @return
	 */
	private Size getBestPreviewSize() {
		Log.v(TAG,"getBestPreviewSize>>");
		Size setPVSize = null;
		screenResolution = CommonUtil.getScreenResolution(wm);
		if (camera != null) {
			Camera.Parameters parameters = camera.getParameters();
			List<Size> previewSizes = camera.getParameters().getSupportedPreviewSizes();
			Log.i(TAG, "camera support parameters is ");
			List<Size> ratioSrceenSizes = new ArrayList<Camera.Size>();
			// Size setPSize = null;
			Size size = null;
			Log.i(TAG, "---------------------");
			int sWidth = screenResolution.x, sHeight = screenResolution.y;
			if (!isLandSpace()) {
				sWidth = screenResolution.y;
				sHeight = screenResolution.x;
			}
			Log.i(TAG,"screen>>"+sWidth+","+sHeight);
			for (int i = 0; i < previewSizes.size(); i++) {
				size = previewSizes.get(i);
				Log.i(TAG, "PreviewSize,width: " + size.width + " height:" + size.height);
				boolean isRatio = CommonUtil.isRatio(sWidth, sHeight, size.width, size.height);
				if (isRatio) {
					ratioSrceenSizes.add(size);
				} else {
					if (size.width <= sWidth + 20 && size.height < sHeight + 20) {
						if (setPVSize == null) {
							setPVSize = size;// update
						} else {
							if (size.width >= setPVSize.width && size.height >= setPVSize.height) {
								setPVSize = size;
							}
						}
					}
				}

			}
			if (ratioSrceenSizes.isEmpty()) {
				return setPVSize;
			} else {
				Log.i(TAG, "find ratio preview size");
				setPVSize = null;
				int pvWidth, pvHeight = 0;
				for (int i = 0; i < ratioSrceenSizes.size(); i++) {
					size = ratioSrceenSizes.get(i);
					pvWidth = size.width;
					pvHeight = size.height;
					Log.i(TAG, "screen w>" + sWidth + " screen h>" + sHeight);
					Log.i(TAG, "PV w>" + pvWidth + " PV h>" + pvHeight);
					if (sWidth == pvHeight && sWidth == pvWidth) {
						setPVSize = size;
						break;
					} else {
						if (setPVSize == null) {
							setPVSize = size;
						} else {
							if (setPVSize.height <= size.height && setPVSize.width <= size.width) {
								setPVSize = size;
							}
						}
					}
				}
			}
		}
		return setPVSize;

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		if (surfaceView == null) {
			return;
		}
		if (camera == null) {
			return;
		}
		try {
			Log.i(TAG, "surfaceCreated>>");
			// 开启摄像头（2.3版本后支持多摄像头,需传入参数）
			// Log.v(TAG, "Camera getNumberOfCameras >>" + Camera.getNumberOfCameras());
			if (hasCameraHardware()) {
				// camera = Camera.open(openCameraId);
				// set the surface to be used for live preview

				camera.setPreviewDisplay(surfaceView.getHolder());
			} else {
				throw new NullPointerException("device has not camera feature");
			}
		} catch (Exception ex) {
			if (null != camera) {
				camera.release();
				camera = null;
			}
			// Log.i(TAG + "initCamera", ex.printStackTrace());
			ex.printStackTrace();
		}

	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		Log.i(TAG, "surfaceChanged>>>" + width + "*" + height);
		surfaceWidth = width;
		surfaceHeight = height;
		setupCamera(bestPreviewSize);

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.i(TAG, "Surface Destroyed>>");
		releaseCamera();
	}

	public void startPreivew() {
		if (camera != null) {
			camera.startPreview();
			hasStartPrevious = true;
		}
	}

	public void stopPreView() {
		if (camera != null) {
			camera.stopPreview();

		}
		hasStartPrevious = false;
	}

	public boolean isLandSpace() {
		return getOrientation() == Configuration.ORIENTATION_LANDSCAPE ? true : false;
	}

	public int getOrientation() {
		return this.context.getResources().getConfiguration().orientation;
	}

	private void releaseCamera() {
		if (camera != null) {
			if (listener != null) {
				listener.beforeReleseCamera();
			}
			camera.cancelAutoFocus();
			camera.autoFocus(null);
			camera.setPreviewCallback(null); // ！！这个必须在前，不然退出出错
			camera.stopPreview();
			hasStartPrevious = false;
			hasInitCamera = false;
			camera.release();
			camera = null;
		}
		if (surfaceView != null) {
			surfaceView.getHolder().removeCallback(this);
		}
	}

	public void setCameraManagerListener(CameraManagerListener listener) {
		this.listener = listener;
	}

	public Camera getCamera() {
		return camera;
	}

	public Parameters getCameraParameters() {
		if (camera != null) {
			return camera.getParameters();
		} else {
			return null;
		}
	}

	public boolean isStartPrevious() {
		return hasStartPrevious;
	}

	public boolean isInitCamera() {
		return hasInitCamera;
	}

	public void setFrameRect(Rect frameRect) {
		this.frameRect = frameRect;
	}

	public Rect getFrameRect() {
		return frameRect;
	}

	private void setupFrameRect() {
		surfaceWidth = surfaceView.getWidth();
		surfaceHeight = surfaceView.getHeight();
		surfaceTop = surfaceView.getTop();
		surfaceLeft = surfaceView.getLeft();
		int frameRectWidth = 0;
		int frameRectHeight = 0;
		int frameRectTop = 0;
		int frameRectLeft = 0;
		if (isLandSpace()) {
			frameRectHeight = (int) (surfaceHeight * 0.7);
			frameRectWidth = frameRectHeight;

		} else {
			frameRectWidth = (int) (surfaceWidth * 0.7);
			frameRectHeight = frameRectWidth;
		}
		frameRectTop = surfaceTop + ((surfaceHeight - frameRectHeight) / 2);
		frameRectLeft = surfaceLeft + ((surfaceWidth - frameRectWidth) / 2);
		if (frameRect == null) {
			frameRect = new Rect();
		}
		frameRect.top = frameRectTop;
		frameRect.left = frameRectLeft;
		frameRect.right = frameRect.left + frameRectWidth;
		frameRect.bottom = frameRect.top + frameRectHeight;
		Log.d(TAG, "frameRect>>" + frameRect);
	}

	/**
	 * adjust device have front camera
	 * 
	 * @return
	 */
	public boolean hasCameraHardware() {
		if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
			// this device has a camera
			return true;
		} else {
			// no camera on this device
			return false;
		}
	}

	public boolean hasCameraAutoFocusHardware() {
		if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS)) {
			// this device has a camera
			return true;
		} else {
			// no camera on this device
			return false;
		}
	}

	/**
	 * adjust device have front camera
	 * 
	 * @return
	 */
	public boolean hasFrontCameraHardware() {
		if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
			// this device has a front camera
			return true;
		} else {
			// no camera on this front device
			return false;
		}
	}

	public Size getCurrentPreviewSize() {
		if (isInitCamera()) {
			return camera.getParameters().getPreviewSize();
		} else {
			return null;
		}
	}

	public Bitmap getFrameBitmap(byte[] data, int width, int height, CameraActionListener listener) {
		Bitmap frameBitmap = null;
		try {
			// Log.v(TAG, "data width>>" + width + " height>>" + height);
			// Log.v(TAG, "surface width>>" + surfaceWidth + " height>>" + surfaceHeight);
			// Log.v(TAG, "----------------");
			// Bitmap yuv2bitmap = rawByteArray2RGBABitmap(data, width, height);
			Bitmap yuv2bitmap = rawByteArray2GrayBitmap(data, width, height);
			int angle = getCameraOrientation();
			// Bitmap rotateBitmap = rotate(yuv2bitmap, angle);
			Matrix matrix = new Matrix();

			matrix.postRotate(angle, width / 2, height / 2);
			// if (isOpenFrontCamera()) {
			// // front camera 解析出嚟bitmap需要镜像垂直翻转,但会decode吾到
			// matrix.postScale(1, -1);// 镜像垂直翻转 decode 吾到
			// }
			int sfwidth = isLandSpace() ? surfaceWidth : surfaceHeight;
			int sfhight = isLandSpace() ? surfaceHeight : surfaceWidth;
			if (sfwidth != width || sfhight != height) {
				float ws = 0.00f;
				float hs = 0.00f;
				ws = (float) sfwidth / (float) width;
				hs = (float) sfhight / (float) height;
				// Log.i(TAG, "ws>>" + ws + ",hs>>" + hs);
				matrix.postScale(ws, hs);
			}

			Bitmap srBitmap = null;
			srBitmap = Bitmap.createBitmap(yuv2bitmap, 0, 0, width, height, matrix, true);
			yuv2bitmap.recycle();
			// Log.v(TAG, "scale Bitmap width>>" + srBitmap.getWidth() + " height>>" + srBitmap.getHeight());
			// Log.v(TAG, "frame rect>>" + frameRect);
			// Log.i(TAG, "surface " + surfaceLeft + "," + surfaceTop + " - " + (surfaceLeft + surfaceWidth) + "," + (surfaceTop + surfaceHeight));
			frameBitmap = Bitmap.createBitmap(srBitmap, frameRect.left - surfaceLeft, frameRect.top - surfaceTop, frameRect.width(), frameRect.height());
			srBitmap.recycle();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			return frameBitmap;
		}
	}

	public Bitmap rawByteArray2RGBABitmap(byte[] data, int width, int height) {
		int[] rgba = rawByteArray2RGBIntArray(data, width, height);

		Bitmap bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		bmp.setPixels(rgba, 0, width, 0, 0, width, height);
		return bmp;
	}

	public Bitmap rawByteArray2GrayBitmap(byte[] data, int width, int height) {
		int[] gray = rawByteArray2GrayIntArray(data, width, height);
//		int[] gray = OpenCVUtil.yuv2gray(data, width, height);
		

		Bitmap bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		bmp.setPixels(gray, 0, width, 0, 0, width, height);
		return bmp;
	}

	public int[] rawByteArray2RGBIntArray(byte[] data, int width, int height) {
		int frameSize = width * height;
		int[] rgba = new int[frameSize];

		for (int i = 0; i < height; i++)
			for (int j = 0; j < width; j++) {
				int y = (0xff & ((int) data[i * width + j]));
				int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
				int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
				y = y < 16 ? 16 : y;

				int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
				int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
				int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));

				r = r < 0 ? 0 : (r > 255 ? 255 : r);
				g = g < 0 ? 0 : (g > 255 ? 255 : g);
				b = b < 0 ? 0 : (b > 255 ? 255 : b);

				rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
			}

		return rgba;
	}

	public int[] rawByteArray2GrayIntArray(byte[] data, int width, int height) {
		int frameSize = width * height;
		int[] gray = new int[frameSize];

		for (int i = 0; i < height; i++)
			for (int j = 0; j < width; j++) {
				int y = (0xff & ((int) data[i * width + j]));
				y = y < 16 ? 16 : y;
				gray[i * width + j] = 0xff000000 | (y << 16) | (y << 8) | y;
			}

		return gray;
	}

	public int getDeviceRotation() {
		if (wm != null) {
			return wm.getDefaultDisplay().getRotation();
		} else {
			return 0;
		}
	}

	public int getCameraOrientation() {
		int degrees = 0;
		switch (getDeviceRotation()) {
		case Surface.ROTATION_0:
			degrees = 0;
			break;
		case Surface.ROTATION_90:
			degrees = 90;
			break;
		case Surface.ROTATION_180:
			degrees = 180;
			break;
		case Surface.ROTATION_270:
			degrees = 270;
			break;
		}
		int result = degrees;
		if (Build.VERSION.SDK_INT <= 8) {
			return result + 90;
		} else {
			CameraInfo info = new CameraInfo();
			Camera.getCameraInfo(openCameraId, info);

			if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
				result = (info.orientation + degrees) % 360;
				result = (360 - result) % 360;
			} else {
				result = (info.orientation - degrees + 360) % 360;
			}
			// Log.i(TAG, "getCameraOrientation>>" + result + " CameraInfo>>" + info.orientation);
		}
		// Log.i(TAG, "getCameraOrientation>>" + result);
		return result;
	}

	public boolean isOpenFrontCamera() {
		return openCameraId == 1 ? true : false;
	}

	public int getSurfaceHeight() {
		return surfaceHeight;
	}

	public int getSurfaceWidth() {
		return surfaceWidth;
	}
	
	
	
}
